<?php defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * 反射类
 * @author 张皓旻
 *
 */
class Elo_reflection
{
    /**
     * 获取公有属性
     * @var int
     */
    const IS_PUBLIC = ReflectionProperty::IS_PUBLIC;
    
    /**
     * 获取受保护的属性
     * @var int
     */
    const IS_PROTECTED = ReflectionProperty::IS_PROTECTED;
    
    /**
     * 获取私有属性
     * @var int
     */
    const IS_PRIVATE = ReflectionProperty::IS_PRIVATE;
    
    /**
     * 存放实例化的反射类
     * @var array
     */
    static private $_ref_classes = array();
    
    /**
     * 验证类名的合法性, 并实现单例
     * @param string $method
     * @param array $params
     * @return mixed
     */
    static public function __callstatic($method, $params)
    {
        $class_name = $params[0];
        
        if ( ! class_exists($class_name))
        {
            show_error('class not exists');
        }
        
        self::$_ref_classes[$class_name] = isset(self::$_ref_classes[$class_name])
            ? self::$_ref_classes[$class_name]
            : new ReflectionClass($class_name);
        
        return call_user_func_array(array(get_called_class(), $method), $params);
    }
    
    /**
     * 批量获取类中的属性
     * @param string $class_name 类名
     * @param object $object 该类实例化出的对象
     * @param string $property_status [IS_PUBLIC|IS_PROTECTED|IS_PRIVATE], 不填写则获取所有属性
     * @return array
     */
    static private function get_properties($class_name, $object, $property_status = NULL)
    {
        if ( ! is_object($object) OR get_class($object) !== $class_name)
        {
            show_error('the object should be instance of '.$class_name);
        }
        
        $accessible = FALSE;
        
        if ($property_status === self::IS_PRIVATE OR $property_status === self::IS_PROTECTED OR $property_status === NULL)
        {
            $accessible = TRUE;
        }
        
        $refc = self::$_ref_classes[$class_name];
        
        if ($property_status === NULL)
        {
            $refps = $refc->getProperties();
        }
        else
        {
            $refps = $refc->getProperties($property_status);
        }
        
        $props = array();
        
        foreach ($refps as $refp)
        {
            $refp->setAccessible($accessible);
            $props[$refp->getName()] = $refp->getValue($object);
        }
        
        return $props;
    }
    
    /**
     * 获取单个属性
     * @param string $class_name
     * @param object $object 该类实例化出的对象
     * @param string $property_name
     */
    static private function get_property($class_name, $object, $property_name)
    {
        if ( ! is_object($object) OR get_class($object) !== $class_name)
        {
            show_error('the object should be instance of '.$class_name);
        }
        
        $refc = self::$_ref_classes[$class_name];
        $refp = $refc->getProperty($property_name);
        $refp->setAccessible(TRUE);
        return $refp->getValue($object);
    }
    
    /**
     * 检查方法是不是只属于该类(未在父类中出现)
     * @param string $class_name
     * @param string $method
     * @return boolean
     */
    static private function has_own_method($class_name, $method)
    {
        $refc = self::$_ref_classes[$class_name];
        
        if ( ! $refc->hasMethod($method))
        {
            return FALSE;
        }
        
        $refm = $refc->getMethod($method);
        
        try
        {
            $refm->getPrototype();
        }
        catch (ReflectionException $e)
        {
            return TRUE;
        }
        
        return FALSE;
    }
    
    /**
     * 调用类中受保护或私有方法
     * @param string $class_name
     * @param object $object 该类实例化出的对象
     * @param string $method
     * @param array $params
     */
    static private function call_private_method($class_name, $object, $method, Array $params = array())
    {
        $refc = self::$_ref_classes[$class_name];
        
        if ( ! $refc->hasMethod($method))
        {
            show_error('undefined method');
        }
        
        if ( ! is_object($object) OR get_class($object) !== $class_name)
        {
            show_error('the object should be instance of '.$class_name);
        }
        
        $refm = $refc->getMethod($method);
        $refm->setAccessible(TRUE);
        return $refm->invokeArgs($object, $params);
    }
}